﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using System.Windows.Forms;
using System.Threading;
using System.Collections.Concurrent;

namespace SynchronizedPlayer
{
    class NetSender
    {
        IPEndPoint _ipEndPoint;
        Socket _sender;
        //Thread _actor;
        bool _sending = false;
        //bool _execute = true;
        ConcurrentQueue<Command> _outQueue = new ConcurrentQueue<Command>();

        ManualResetEvent _connectDone = new ManualResetEvent(false);
        ManualResetEvent _sendDone = new ManualResetEvent(false);
        ManualResetEvent _disconnectDone = new ManualResetEvent(false);

        public NetSender(string IP, int port)
        {
            _ipEndPoint = new IPEndPoint(IPAddress.Parse(IP), port);
            _sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);

            //_actor = new Thread(new ThreadStart(sendLoop));
            // _actor.Start();
        }
        //private void sendLoop()
        //{ 
        //    while (_execute)
        //        while (!_outQueue.IsEmpty)
        //        {
        //            while (_sending) Thread.Sleep(100);
        //            Command c;
        //            if (_outQueue.TryDequeue(out c))
        //            {
        //                Send(c);
        //            }
        //            else _actor.Suspend();
        //        }
        //}
        public void Enqueue(Command cmd)
        {
            try
            {
                _outQueue.Enqueue(cmd);
                if (!_sending)
                {
                    Command c;
                    if (_outQueue.TryDequeue(out c))
                    {
                        Send(c);
                    }
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.StackTrace);
            }
            //if (_actor.ThreadState == System.Threading.ThreadState.Suspended)
            //{
            //    _actor.Resume();
            //}
        }
        public void Send(Command cmd)
        {
            _sender.BeginConnect(_ipEndPoint, connectCallback, new Tuple<Socket, Command>(_sender, cmd));
            _sending = true;
            _connectDone.WaitOne();
        }
        private void connectCallback(IAsyncResult res)
        {
            try
            {
                //MessageBox.Show("send:connectcallback");
                Socket client = (Socket)((Tuple<Socket, Command>)res.AsyncState).Item1;


                Command sendcmd = (Command)(((Tuple<Socket, Command>)res.AsyncState).Item2);
                client.EndConnect(res);
                _connectDone.Set();

                var bytes = sendcmd.ToByteArray();
                _sender.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, sendCallback, _sender);
                _sendDone.WaitOne();

            }
            catch (Exception e)
            {
                MessageBox.Show(e.StackTrace);
            }
        }


        private void sendCallback(IAsyncResult res)
        {
            //MessageBox.Show("send:sendcallback");
            try
            {
                Socket handler = (Socket)(res.AsyncState);
                handler.EndSend(res);

                _sendDone.Set();
                handler.BeginDisconnect(true, disconnectCallback, handler);
                _disconnectDone.WaitOne();
            }
            catch (Exception e)
            {
                MessageBox.Show(e.StackTrace);
            }
            // handler.BeginDisconnect( true, disconnectCallback, handler );

        }
        private void disconnectCallback(IAsyncResult res)
        {
            //MessageBox.Show("send:discocallback");
            try
            {
                Socket handler = (Socket)(res.AsyncState);
                handler.EndDisconnect(res);

                Command c;
                if (_outQueue.TryDequeue(out c))
                {
                    _sendDone.Reset();
                    _connectDone.Reset();

                    _sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
                    _sender.BeginConnect(_ipEndPoint, connectCallback, new Tuple<Socket, Command>(_sender, c));
                    _connectDone.WaitOne();
                }
                else
                {
                    _sending = false;
                    _sendDone.Set();
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.StackTrace);
            }

        }

        public void Dispose()
        {
            if (_sender != null) _sender.Close();
            //_execute = false;
            //if (_actor.ThreadState == System.Threading.ThreadState.Suspended) _actor.Resume();
        }

    }
}
